package com.zys.el.time.ga;

import cn.hutool.core.util.RandomUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.util.*;

/**
 * 种群个体
 */
@EqualsAndHashCode(callSuper = true)
@Data
@NoArgsConstructor
public class Schedule extends Task{
    private final long serialVersionUID = RandomUtil.randomLong();

    @Override
    public String toString() {
        return "Schedule{" +
                "slot=" + slot +
                ", lenSlot=" + lenSlot +
                ", taskId='" + taskId + '\'' +
                ", time=" + time +
                '}';
    }

    /**
     * 基因序列
     */
    private List<Integer> slot; // 索引：0~47, 48~106, 107~130

    // 影响因子
    private int lenSlot;

    public Schedule(String taskId, int time) {
        this.taskId = taskId;
        this.time = time;
    }

    public Schedule(String taskId, List<Integer> slot, int time, int lenSlot) {
        super(taskId, time);
        this.slot = slot;
        this.lenSlot = lenSlot;
    }

    public Schedule(List<Integer> slot, int lenSlot) {
        this.slot = slot;
        this.lenSlot = lenSlot;
    }

    public Schedule(Schedule s) {
        this.taskId = s.taskId;
        this.slot = new ArrayList<>(s.getSlot());
        this.time = s.time;
        this.lenSlot = s.lenSlot;
    }

    public Schedule(String teachTaskId) {
        this.taskId = teachTaskId;
    }

    // 初始化
    void random_init(SchedulePopulation sp) {
        Random random = new Random();

        int idx = random.nextInt(sp.scheduleNum);
        lenSlot = time / sp.timeInterval;

        ArrayList<Integer> integers = new ArrayList<>();
        if (lenSlot + idx >= sp.scheduleNum)
            idx -= lenSlot + idx - sp.scheduleNum + 1;
        for (int i = 0; i < lenSlot; i++) {
            integers.add(idx++);
        }
        this.slot = integers;
    }

    // 是否冲突
    public static int hasConflict(List<Schedule> schedules) {
        int res = 0;
        for (int i = 0; i < schedules.size(); i++) {
            Schedule schedule1 = schedules.get(i);
            List<Integer> s1 = schedule1.getSlot();
            int size1 = s1.size();

            for (int j = i + 1; j < schedules.size(); j++) {
                Schedule schedule2 = schedules.get(j);
                List<Integer> s2 = schedule2.getSlot();

                int size2 = s2.size();
                if ((s1.get(0) > s2.get(0) && s1.get(0) < s2.get(size2 - 1))
                        || (s1.get(size1 - 1) > s2.get(0) && s1.get(size1 - 1) < s2.get(size2 - 1))
                        || (s1.get(0) < s2.get(0) && s1.get(size1 - 1) > s2.get(size2 - 1))
                        || (s1.get(0) > s2.get(0) && s1.get(size1 - 1) < s2.get(size2 - 1))

                ) {
                    res++;
                }
            }
        }
        return res;
    }

    /**
     * 硬性规则（必须满足）
     *
     * @param map        计分集合
     * @param population 种群集
     */
    public static void checkHardness(Map<List<Schedule>, Double> map, List<List<Schedule>> population) {
        // 计算冲突，有冲突，适应度为0
        for (List<Schedule> schedules : population) {
            map.put(schedules, hasConflict(schedules) > 0 ? 0 : 1 / 5D);
        }
    }

    /**
     * 设定时间与安排时间误差范围
     */
    public static final int limitRange = 7;

    // 是否在设定时间上下范围内
    public static int isSetTimeRange(List<Schedule> schedules) {
        int res = 0;
        for (Schedule sc : schedules) {
            int time = sc.getLenSlot() * 5;
            if (Math.abs(sc.getTime() - time) >= limitRange) {
                res++;
            }
        }
        return res;
    }

    // 全天饱满度
    public static double wholeDayFullness(List<Schedule> schedules, SchedulePopulation sp) {
        HashSet<Integer> set = new HashSet<>();
        for (Schedule schedule : schedules) {
            set.addAll(schedule.getSlot());
        }
        return (double) set.size() / sp.scheduleNum;
    }

}